//STL
#include <stdexcept>
#include <string>
#include <iostream>

//Graphics
#include "Exceptions/GLError.h"
#include "Exceptions/InvalidGLId.h"
#include "Renderable.h"

//Misc
#include "../Utilities.h"

using namespace Graphics;

Renderable::Renderable(Renderable* p, bool d)
{
	_id=0;
	_texture=NULL;
	_redraw=true;
	_dynamic=d;
	_parent=p;
}

/**********
* GETTERS *
**********/

unsigned int Renderable::id() const
{
	return _id;
}

Texture* Renderable::texture() const
{
	return _texture;
}

Renderable* Renderable::parent() const
{
	return _parent;
}

bool Renderable::willRedraw() const
{
	return _redraw;
}

Point* Renderable::position()
{
	return &_position;
}

Quaternion* Renderable::rotation()
{
	return &_rotation;
}

/**********
* SETTERS *
**********/
void Renderable::setTexture(Texture* tex)
{
	_texture=tex;
}

void Renderable::setParent(Renderable* d)
{
	_parent=d;
}

void Renderable::setDynamic(bool d)
{
	_dynamic=d;

	//Regenerate display list ID if needed
	if (!_dynamic && _id<1)
	{
		_id=glGenLists(1);

		if (_id<1)
		{
			if (InvalidGLId::checkErrors())
				std::cout << (new InvalidGLId(this,"asdF"))->message();

			throw new InvalidGLId(this,"Cannot regenerate the display list id.");
		}
	}
	else
	{
		//Allow OpenGL to recycle list id numbers
		if (_id>0)
		{
			glDeleteLists(_id,1);
			_id=0;
		}
	}

	//Make sure this object gets redrawn
	_redraw=true;
}

void Renderable::setRedraw(bool r)
{
	_redraw=r;
}

void Renderable::setPosition(const Point& pos)
{
	_position=pos;
	_redraw=true;
}

void Renderable::setRotation(const Quaternion& rot)
{
	_rotation=rot;
	_redraw=true;
}

//Rotation
void Renderable::rotateX(float amount)
{
	Vector* v=_rotation.direction();
	*v+=Vector::X;
	_rotation.setAngle(_rotation.angle()+amount);
	_redraw=true;
}

void Renderable::rotateY(float amount)
{
	Vector* v=_rotation.direction();
	*v+=Vector::Y;
	_rotation.setAngle(_rotation.angle()+amount);
	_redraw=true;
}

void Renderable::rotateZ(float amount)
{
	Vector* v=_rotation.direction();
	*v+=Vector::Z;
	_rotation.setAngle(_rotation.angle()+amount);

	_redraw=true;
}

//Translation
void Renderable::move(const Point& pos)
{
	_position=pos;
	_redraw=true;
}

void Renderable::move(const Vector& amount)
{
	_position+=amount;
	_redraw=true;
}

/**********
* DRAWING *
**********/
void Renderable::draw()
{

	std::cout<< "Renderable::draw() called" << std::endl;

	if (_dynamic || _redraw)
		redraw();

	if (_id)
		render();

	//No children? No more rendering.
	if (_children.empty())
		return;

	//Loop through all the children and call their draw() method
	ListT::iterator it;

	for (it=_children.begin();it!=_children.end();it++)
		(*it)->draw();
}

void Renderable::render()
{
	glCallList(_id);
}


/*************
* COLLECTION *
*************/
void Renderable::add(Renderable* d)
{
	bool found=false;
	ListT::iterator it;

	for (it=_children.begin();it!=_children.end();it++)
	{
		if (*it==d)
		{
			found=true;
			break;
		}
	}

	//only insert the child if it's not already in the list
	if (!found)
	{
		_children.push_front(d);
		d->setParent(this);
	}
}

void Renderable::remove(Renderable* d)
{
	_children.remove(d);
}

int Renderable::count()
{
	//return _children.size();
	return 0;
}

Renderable::ListT* Renderable::children()
{
	return &_children;
}

void Renderable::createID()
{
	if (_id>0)
		return;

	_id=glGenLists(1);

	if (_id<1)
		throw new InvalidGLId(this,"Unable to create a new OpenGL DisplayList ID");
}
